//
// Created by cube on 2020/9/15.
//

#ifndef COMPILERDOMEA1_PEER_H
#define COMPILERDOMEA1_PEER_H

#include <iostream>
#include <memory>

#include "../lib/Base/Object.h"
#include "../lib/Platform/NetBase.h"
#include "../lib/Base/AOP_IOC.h"

#include "CommonNetBase/Window.h"
#include "CommonNetBase/Buffer.h"
#include "CommonNetBase/Controller.h"
#include "Agreement.h"
#include "Option.h"

using namespace NetBase;
using namespace NetBaseCommon;

using Frame_Id = uint8_t;
struct Frame {
    int8_t* data;
    uint32_t len;
    virtual ~Frame(){if(data != nullptr)delete data;}
};

class Peer : public SendController<Frame*,Frame_Id> , public Lib::EventDriver {
public:
//    LIB_OBJECT
    struct SendToIO_Event : public Event{
        LIB_EVENT
        NetAddress  &addr;
        Frame*      frame;
        SendToIO_Event(Frame* const& _frame,NetAddress  &_addr):frame(_frame),addr(_addr){}
        ~SendToIO_Event(){delete frame;}
    };

    struct SendAckToIO_Event : public Event{
        LIB_EVENT
        NetAddress  &addr;
        Frame_Id    id;
        Frame*      frame;
        SendAckToIO_Event(Frame_Id const& _id,Frame* const& _frame,NetAddress  &_addr)
        :id(_id),frame(_frame),addr(_addr){}
        ~SendAckToIO_Event(){delete frame;}
    };

    struct SendTimeOut_Event : public Event{
        LIB_EVENT
        NetAddress  &addr;
        SendTimeOut_Event(NetAddress& _addr):addr(_addr){}
    };
protected:
    struct Time_Event : public ObjectEvent{
        Frame_Id id;
        Frame_Ack* frame;
        Time_Event(SendController* _source,Frame_Id _id,Frame_Ack* _frame)
        :ObjectEvent(_source),id(_id),frame(_frame){}
    };

    struct SendFrame_TimeOut_Event : public Time_Event{
        LIB_EVENT
        SendFrame_TimeOut_Event(SendController* _source,Frame_Id _id,Frame_Ack* _frame)
        :Time_Event(_source,_id,_frame){}
    };

    struct Time_SendAckFrame_Event : public Time_Event{
        LIB_EVENT
        Time_SendAckFrame_Event(SendController* _source,Frame_Id _id,Frame_Ack* _frame)
        :Time_Event(_source,_id,_frame){}
    };

    struct Time_SendFrame_Event : public ObjectEvent{
        LIB_EVENT
        Frame*         frame;
        uint32_t        size;
        Time_SendFrame_Event(SendController* _source,Frame* const& _frame,const uint32_t& _size)
        :ObjectEvent(_source),frame(_frame),size(_size){}
    };
public:
    Peer(const NetBaseCommon::Option& option,Object* parent = nullptr)
    :SendController(option.__send_timeout,option.__max_send_count,option.__defaule_pacing_rate
            ,option.__default_rul_index,option.__min_frame_index,option.__min_frame_index,option.__init_send_wnd_size)
            ,__window(option.__min_frame_index,option.__max_frame_index,option.__default_rul_index,nullptr)
            ,__option(option){}

    void clear(){
        SendController::clear();
        __window.clear();
        if(__recv_buffer != nullptr) __recv_buffer->clean();
        if(__send_buffer != nullptr) __send_buffer->clean();
        while (!__cmds.empty()) __cmds.pop();
        while (!__cmds.empty()) __acks.pop();
    }

    void onAck(const Frame_Id& id){
        if(!__frames.contains(id))return;
        SendController::onAck(id,__frames[id]->len);
        __frames.erase(id);
    }

    Frame_Id sendFrame(Frame* const& frame,const uint32_t& size){
        Frame_Id id = SendController::sendFrame(frame,size);
        __frames[id] = frame;
        return id;
    }

    void writeToBuffer(const Frame_Id & id,Frame* frame){
        if(__window.canRecv()){
            Frame* f = __window.recv();
            if(__recv_buffer == nullptr)__recv_buffer = new IndexBuffer(__option.__max_recv_buffer_size);
            __recv_buffer->write(f->data,f->len);
            delete f;
        }
        __window.put(id,frame);
    }

    uint32_t readFromBuffer(int8_t* data,const uint32_t& len){
        if(__send_buffer == nullptr)return 0;
        return __send_buffer->read(data,len);
    }

    uint32_t recv(int8_t* data,const uint32_t& len){
        if(__recv_buffer == nullptr)return 0;
        __recv_buffer->read(data,len);
    }

    uint32_t send(int8_t* data,const uint32_t& len){
        if(__send_buffer == nullptr)__send_buffer = new IndexBuffer(__option.__max_send_buffer_size);
        return __send_buffer->write(data,len);
    }

    bool empty(){
        if(!__acks.empty())return false;
        if(!__cmds.empty())return false;
        if(__send_buffer != nullptr)if(!__send_buffer->empty())return false;
        return true;
    }

    NetAddress                  __addr;
    RecvWindow<Frame*,Frame_Id> __window;
    Buffer*                     __recv_buffer;
    Buffer*                     __send_buffer;
    std::queue<Frame_Id >       __acks;
    std::queue<CommandBase*>    __cmds;
    std::map<Frame_Id ,Frame*>  __frames;

protected:
    void sendToIO(const Frame_Id& id,Frame* const& frame) override {
        std::cout<<"send ack frame to io "<<(int)id<<std::endl;
        sendEvent(new SendAckToIO_Event(id,frame,__addr));}
    void sendToIO(Frame* const& frame) override {
        std::cout<<"send frame to io "<<std::endl;
        sendEvent(new SendToIO_Event(frame,__addr));}
    void sendTimeout() override {
        std::cout<<"send ack frame "<<std::endl;
        sendEvent(new SendTimeOut_Event(__addr));}

    void timeEvent_sendFrameAck(std::chrono::microseconds time,Frame_Id id,Frame_Ack *frame) override {
        std::cout<<"send ack frame "<<(int)id<<std::endl;
        sendEvent(time,new Time_SendAckFrame_Event(this,id,frame));}
    void timeEvent_sendTimeout(std::chrono::microseconds time,Frame_Id id,Frame_Ack *frame) override {
        sendEvent(time,new SendFrame_TimeOut_Event(this,id,frame));}
    void timeEvent_sendFrame(std::chrono::microseconds time,Frame* const& frame,const uint32_t& size) override {
        std::cout<<"send frame"<<std::endl;
        sendEvent(time,new Time_SendFrame_Event(this,frame,size));}

    void event(EventAgent& event) override {
        if(event->getType() == SendFrame_TimeOut_Event::type()){
            auto _eve = (SendFrame_TimeOut_Event*)event.data();
            std::cout<<"time out "<<(int)_eve->id<<std::endl;
            if(_eve->source_object == this)ackTimeout(_eve->id,_eve->frame);
        }else if(event->getType() == Time_SendAckFrame_Event::type()){
            auto _eve = (Time_SendAckFrame_Event*)event.data();
            if(_eve->source_object == this)sendFrameAck(_eve->id,_eve->frame);
        }else if(event->getType() == Time_SendFrame_Event::type()){
            auto _eve = (Time_SendFrame_Event*)event.data();
            if(_eve->source_object == this)sendFrameOnly(_eve->frame,_eve->size);
        }
    }

private:
    const NetBaseCommon::Option& __option;

};
#endif //COMPILERDOMEA1_PEER_H
